﻿namespace PillBox.Interactivity
{
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Media;

    public class SwipeGesture : GoToStateBase
    {
        private double _originalTranslateX;

        #region ChangeDelta Dependency Property
        /// <summary>
        /// Identifies the ChangeDelta dependency property.
        /// </summary>
        public static readonly DependencyProperty ChangeDeltaProperty =
            DependencyProperty.Register(
                "ChangeDelta",
                typeof(double),
                typeof(SwipeGesture),
                new PropertyMetadata(165.0, OnChangeDeltaChanged));

        /// <summary>
        /// Gets or sets the ChangeDelta.
        /// </summary>
        /// <value>The value of ChangeDelta.</value>
        public double ChangeDelta
        {
            // IMPORTANT: To maintain parity between setting a property in
            // XAML and procedural code, do not touch the getter and setter
            // inside this dependency property!
            get
            {
                return (double)GetValue(ChangeDeltaProperty);
            }

            set
            {
                SetValue(ChangeDeltaProperty, value);
            }
        }

        /// <summary>
        /// Called when ChangeDelta has changed.
        /// </summary>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected virtual void OnChangeDeltaChanged(double oldValue, double newValue)
        {
        }

        /// <summary>
        /// Called when ChangeDelta has changed.
        /// </summary>
        /// <param name="o">The target dependency object.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnChangeDeltaChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            SwipeGesture swipeGesture = o as SwipeGesture;
            if (swipeGesture != null)
            {
                swipeGesture.OnChangeDeltaChanged((double)e.OldValue, (double)e.NewValue);
            }
        }
        #endregion

        #region Velocity Dependency Property
        /// <summary>
        /// Identifies the Velocity dependency property.
        /// </summary>
        public static readonly DependencyProperty VelocityProperty =
            DependencyProperty.Register(
                "Velocity",
                typeof(double),
                typeof(SwipeGesture),
                new PropertyMetadata(1.0, OnVelocityChanged));

        /// <summary>
        /// Gets or sets the Velocity.
        /// </summary>
        /// <value>The value of Velocity.</value>
        public double Velocity
        {
            // IMPORTANT: To maintain parity between setting a property in
            // XAML and procedural code, do not touch the getter and setter
            // inside this dependency property!
            get
            {
                return (double)GetValue(VelocityProperty);
            }

            set
            {
                SetValue(VelocityProperty, value);
            }
        }

        /// <summary>
        /// Called when Velocity has changed.
        /// </summary>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected virtual void OnVelocityChanged(double oldValue, double newValue)
        {
        }

        /// <summary>
        /// Called when Velocity has changed.
        /// </summary>
        /// <param name="o">The target dependency object.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnVelocityChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            SwipeGesture swipeGesture = o as SwipeGesture;
            if (swipeGesture != null)
            {
                swipeGesture.OnVelocityChanged((double)e.OldValue, (double)e.NewValue);
            }
        }
        #endregion

        private CompositeTransform TargetTransform
        {
            get
            {
                return this.Target.RenderTransform as CompositeTransform;
            }
        }

        protected override void Invoke(object parameter)
        {
            SwipeGestureEventArgs args = (SwipeGestureEventArgs)parameter;

            switch (args.ManipulationEvent)
            {
                case ManipulationEvent.Started:
                    OnManipulationStarted((ManipulationStartedEventArgs)args.EventArgs);
                    break;
                case ManipulationEvent.Delta:
                    OnManipulationDelta((ManipulationDeltaEventArgs)args.EventArgs);
                    break;
                case ManipulationEvent.Completed:
                    OnManipulationCompleted((ManipulationCompletedEventArgs)args.EventArgs);
                    break;
            }
        }

        protected virtual void OnManipulationStarted(ManipulationStartedEventArgs e)
        {
            if (this.Target != null && this.TargetTransform != null)
            {
                _originalTranslateX = this.TargetTransform.TranslateX;
            }
        }

        protected virtual void OnManipulationDelta(ManipulationDeltaEventArgs e)
        {
            if (this.Target != null && this.TargetTransform != null)
            {
                this.TargetTransform.TranslateX = _originalTranslateX + (e.CumulativeManipulation.Translation.X * this.Velocity);
            }
        }

        protected virtual void OnManipulationCompleted(ManipulationCompletedEventArgs e)
        {
        }
    }
}